const xmlParse = require('xml2js');
const sip = require('sip');

class TSip {
    constructor() {
        this.sessions = {};
        this.channels = {};
        this.version = "sip-gb28181-20231217";
        this.port = 5060;
        //this.sipHost = '58.216.211.198';
        //this.sipPort = 31285;
    }
    start() {
        var me = this;
        return new Promise((su, fa) => {
            try {
                var opts = {
                    port: me.port,
                    logger: {
                        send: (message, address) => {
                            console.log("==send==:", message, address);
                        },
                        recv: (message, address) => {
                            console.log('==recv==:', message, address);
                            //message.address = address;
                            me.updateSession(message);

                            var isRegister = me.isMsgRegist(message);
                            var isMessage = me.isMsgMessage(message);

                            if (isRegister) {
                                var rs = sip.makeResponse(message, 200, 'OK')
                                console.log('send regist ack.')
                                sip.send(rs);

                                setTimeout(() => {
                                        me.queryDeviceInfoByRegisterMsg(message)
                                    }, 3000)
                                    //me.queryDeviceInfoByRegisterMsg(message);
                            } else if (isMessage) {
                                if (message.content) {
                                    //console.log(message.content);
                                    xmlParse.parseString(message.content, (error, result) => {
                                        //console.log('rec sip message.', result, error);
                                        var cmdType = "";
                                        if (result.Notify) {
                                            cmdType = result.Notify.CmdType[0]
                                        } else if (result.Response) {
                                            cmdType = result.Response.CmdType;
                                        }
                                        if (cmdType == "Keepalive") {
                                            var rs = sip.makeResponse(message, 200, 'OK');
                                            sip.send(rs)
                                        } else if (cmdType == "Catalog") {
                                            //console.log('recive catalog -- deviceList');
                                            me.updateDevices(message)
                                            var rs = sip.makeResponse(message, 200, 'OK');
                                            sip.send(rs)
                                        } else {
                                            console.log('--------- other message -----', message);

                                        }
                                    });
                                }
                            } else if (message.reason == 'OK' && message.headers.cseq.method == "REGISTER") {
                                console.log('-------  regist success --------');
                                //me.queryDeviceInfoByRegisterMsg(message);
                            } else if (message.reason == 'OK' && message.headers.cseq.method == "INVITE") {
                                console.log('-------  invite success --------');
                                var sipId = sip.parseUri(message.headers.from.uri).user;
                                var cseq = message.headers.cseq;
                                cseq.method = "ACK";
                                var via = null;
                                for (var i = 0; i < message.headers.via.length; i++) {
                                    var v = message.headers.via[i];
                                    var viaFlag = v.params && v.params.received && v.params.rport && v.params.rport > 0;
                                    if (viaFlag) {
                                        via = v;
                                        break;
                                    }
                                }
                                var vial = JSON.parse(JSON.stringify(via));
                                var channelId = sip.parseUri(message.headers.to.uri).user;
                                var uri = "sip:" + channelId + "@" + address.address + ":" + address.port;
                                var res = {
                                    method: 'ACK',
                                    uri: uri,
                                    headers: {
                                        via: [
                                            //[vial]
                                        ],
                                        from: {
                                            uri: message.headers.from.uri,
                                            params: message.headers.from.params,
                                        },
                                        to: message.headers.to.uri,
                                        'call-id': message.headers['call-id'],
                                        cseq: cseq,
                                        'Max-Forwards': 70,
                                        'User-Agent': 'nodejs gb server',
                                        contact: [{
                                            // 流媒体服务器的IP和端口
                                            uri: 'sip:' +
                                                sipId +
                                                '@' +
                                                '58.216.211.198' +
                                                ':' +
                                                '10000',
                                        }, ],
                                    },
                                }
                                sip.send(res)

                                //me.queryDeviceInfoByRegisterMsg(message);
                            } else {
                                console.log('**********  other msg ***********', message)
                            }
                        }
                    }
                };
                sip.start(opts, (request) => {
                    me.updateSession(request);
                })
                su();
            } catch (error) {
                console.log(error)
                su();
            }
        })
    }

    updateSession(request) {
        var me = this;
        try {
            var sessionKey = request.headers.from.uri;
            me.sessions[sessionKey] = request;
        } catch (error) {
            console.log(error)
        }
    }
    updateDevices(request) {
        var me = this;
        try {
            xmlParse.parseString(request.content, (error, result) => {
                //console.log(result);
                try {
                    var deviceId = result.Response.DeviceID[0];
                    result.Response.DeviceList.forEach(dev => {
                        dev.Item.forEach(item => {
                            var channelId = item.DeviceID[0];
                            var domain = item.CivilCode[0];
                            var sipId = item.ParentID[0];

                            var channCode = [domain, sipId, deviceId, channelId].join("/")
                            me.channels[channCode] = {
                                domain,
                                sipId,
                                deviceId,
                                channelId,
                                request: request
                            }
                        })
                    });
                    console.log('channels:', me.channels);
                } catch (err) {
                    console.log(err);
                }
            });
        } catch (error) {
            console.log(error)
        }
    }
    inviteChannel(channelCode) {
        var me = this;
        var flag = false;
        try {
            var channelInfo = me.channels[channelCode];
            if (channelInfo) {
                var requsetUri = channelInfo.request.headers.from.uri;
                var request = me.sessions[requsetUri];
                if (request) {
                    var via = null;
                    for (var i = 0; i < request.headers.via.length; i++) {
                        var v = request.headers.via[i];
                        var viaFlag = v.params && v.params.received && v.params.rport && v.params.rport > 0;
                        if (viaFlag) {
                            via = v;
                            break;
                        }
                    }
                    var vial = JSON.parse(JSON.stringify(via))
                    var randomTarget = Math.floor(Math.random() * 1e6).toString();
                    var cseq = request.headers.cseq;
                    cseq.method = "INVITE";
                    var content =
                        'v=0\r\n' +
                        'o=fxdigital 0 0 IN IP4 ' +
                        '58.216.211.198' +
                        '\r\n' +
                        's=Play\r\n' +
                        'c=IN IP4 ' +
                        '58.216.211.198' +
                        '\r\n' +
                        't=0 0\r\n' +
                        'm=video ' +
                        '10000' +
                        ' RTP/AVP 96 97 98\r\n' +
                        'a=sendrecv\r\n' +
                        'a=rtpmap:96 PS/90000\r\n' +
                        'a=rtpmap:97 MPEG4/90000\r\n' +
                        'a=rtpmap:98 H264/90000\r\n' +
                        'y=0000000003\r\n';
                    var res = {
                        method: 'INVITE',
                        uri: 'sip:' + channelInfo.channelId + '@' + via.params.received + ':' + via.params.rport,
                        version: request.version,
                        headers: {
                            via: [
                                [vial]
                            ],
                            from: {
                                uri: 'sip:' + channelInfo.sipId + '@' + channelInfo.domain,
                                params: { tag: randomTarget },
                            },
                            to: { uri: 'sip:' + channelInfo.deviceId + '@' + via.params.received + ':' + via.params.rport }, //TODO: 这里暂时写死
                            //to: { uri: 'sip:' + channelInfo.channelId + '@' + via.host + ':' + via.port }, //TODO: 这里暂时写死
                            'call-id': request.headers['call-id'],
                            cseq: cseq,
                            'Content-Type': 'Application/SDP',
                            expires: '3600',
                            'Max-Forwards': 70,
                            'User-Agent': 'nodejs gb server',
                            contact: [{
                                uri: 'sip:' +
                                    channelInfo.sipId +
                                    '@' +
                                    //me.sipHost +
                                    via.params.received +
                                    //'58.216.211.198' +
                                    ':' +
                                    //me.sipPort,
                                    via.params.rport,
                                //'31285',
                            }, ],
                            subject: channelInfo.channelId + ':1' + ',' + channelInfo.sipId + ':0',
                        },
                        content: content,
                    }
                    sip.send(res);
                    flag = true;
                }
            }
        } catch (error) {
            console.log(error)
        }
        return flag;
    }
    queryDeviceInfoByRegisterMsg(registMsg) {
        var me = this;
        try {
            var respData = {
                method: "MESSAGE",
                uri: "",
                version: registMsg.version,
                headers: {
                    via: registMsg.headers.via,
                    from: registMsg.headers.from,
                    to: registMsg.headers.to,
                    'call-id': registMsg.headers['call-id'],
                    cseq: registMsg.headers.cseq,
                    'Content-Type': 'Application/MANSCDP+xml',
                    expires: '3600',
                    'Max-Forwards': 70,
                },
                content: ""
            };
            var via = null
            for (var i = 0; i < registMsg.headers.via.length; i++) {
                var v = registMsg.headers.via[i];
                var flag = v.params && v.params.received && v.params.received != null && v.params.rport && v.params.rport > 0;
                if (flag) {
                    via = v;
                    break;
                }
            }
            if (via != null) {
                var deviceId = sip.parseUri(registMsg.headers.from.uri).user;
                var json = {
                    Query: {
                        CmdType: 'Catalog',
                        SN: 1,
                        DeviceID: deviceId,
                    },
                }
                const builder = new xmlParse.Builder()
                const content = builder.buildObject(json)
                respData.content = content;
                respData.uri = 'sip:' + deviceId + '@' + via.params.received + ':' + via.params.rport;
                sip.send(respData);
            }
        } catch (error) {
            console.log(error)
        }
    }
    isMsgRegist(request) {
        var me = this;
        var res = false
        try {
            res = request.method == "REGISTER";
        } catch (error) {
            console.log(error)
        }
        return res;
    }
    isMsgMessage(request) {
        var me = this;
        var res = false
        try {
            res = request.method == "MESSAGE";
        } catch (error) {
            console.log(error)
        }
        return res;
    }
}

const sipService = new TSip();

module.exports = {
    TSip,
    sipService
}